home *** CD-ROM | disk | FTP | other *** search
- /* Library for handling menus and the menu bar.
-
- Revision History:
-
- 94/01/09 aih
- - changed command constant prefix from "MC_" to "CMD_"
-
- 93/12/25 aih
- - imported function for outlining the size menu from TextMenuLib.c
-
- 93/12/17 aih
- - made menu table a handle so can dynamically append commands to it
-
- 93/12/02 aih
- - improved efficiency of menu adjustment
-
- 93/03/18 AIH
- - To speed things up, made menu table index by menu number
- - To speed things up, the menu handle is saved in the menu table
-
- 93/03/12 AIH
- - Added functions to replace GetMHandle and to setup the help menu
-
- 92/03/06 AIH
- - Added help menu command
-
- 92/02/25 AIH
- - Added functions to map from menu IDs and item numbers to
- menu command numbers
-
- 92/02/21 AIH
- - Added function to check menus by name
-
- 91/11/15 AIH
- - Uses the calculation (sizeof(long) * CHAR_BIT) to determine the
- number of bits in a long integer instead of hard-coding the number
- 32.
-
- 91/05/14 AIH
- - Added function to determine if a menu item is enabled
-
- 91/04/21 AIH
- - Fixed menu validation under system 7.0
-
- 91/04/06 AIH
- - Added function to create a menu bar and insert all heirarchical menus
- when the menu bar is created
-
- 91/03/10 AIH
- - Added functions for enabling/disabling the entire menu bar
-
- 91/02/15 AIH
- - Added functions for accessing the menu list
- - Added constants for standard menus and menu items
-
- 91/01/05 Ari Halberstadt (AIH)
- - Inserted this standard header in all files */
-
- #include <string.h>
- #include <limits.h>
- #include <Balloons.h>
- #include "KeyLib.h"
- #include "LowMemLib.h"
- #include "MacLib.h"
- #include "MathLib.h"
- #include "MemoryLib.h"
- #include "MenuLib.h"
- #include "ResourceConstantsLib.h"
- #include "ResourceLib.h"
- #include "ScreenLib.h"
- #include "StringLib.h"
-
- static MenuPickType **gMenuTable;/* menu command translation table */
- static short gMenuTableSize; /* number of commands in menu table (actually,
- value of largest command plus 1) */
-
- /*---------------------------------------------------------------------------*/
- /* validation */
- /*---------------------------------------------------------------------------*/
-
- /* true if the menu is a valid handle to a menu; in system 7.0 we can't
- validate the Apple menu since it's in some weird heap zone */
- Boolean MenuValid(MenuHandle menu)
- {
- if (menu && menu == GetMHandle(RLM_APPLE)) return(true);
- return(HandleValidSize(menu, sizeof(MenuInfo) - sizeof(Str255)));
- }
-
- /* true if the ID corresponds to the menu ID of an existing menu */
- Boolean MenuIDValid(short id)
- {
- return(id != 0 && MenuHandleGet(id) != NULL);
- }
-
- /* true if the item is a valid item in the menu with the specified ID */
- Boolean MenuItemValid(short id, short item)
- {
- return(MenuIDValid(id) && 0 <= item && item <= CountMItems(MenuHandleGet(id)));
- }
-
- /* true if the menu command is valid */
- Boolean MenuCmdValid(MenuCommandType cmd)
- {
- return(0 <= cmd && cmd < gMenuTableSize);
- }
-
- /* true if the menu pick is valid */
- Boolean MenuPickValid(const MenuPickType *pick)
- {
- if (pick->menu && ! MenuValid(pick->menu)) return(false);
- if (! MenuIDValid(pick->id)) return(false);
- if (! MenuCmdValid(pick->cmd)) return(false);
- return(true);
- }
-
- /*---------------------------------------------------------------------------*/
- /* Menu commands are handled by using a table containing a mapping
- of all menu ID and item numbers to unique menu command numbers.
- The application passes a specific table, and these functions
- handle the mapping between them. The menu table passed by the
- application is terminated by an entry with a menu ID of zero. */
- /*---------------------------------------------------------------------------*/
-
- /*---------------------------------------------------------------------------*/
- /* menu table maintenance */
- /*---------------------------------------------------------------------------*/
-
- static void MenuSetup(void)
- {
- MenuHandle help = MenuHandleGet(kHMHelpMenuID);
- Str255 title;
- short cmd;
-
- /* move the help menu command to the help menu */
- if (MenuCmdValid(CMD_HELP) && MenuCmdHandle(CMD_HELP)) {
- if (help && help != MenuCmdHandle(CMD_HELP)) {
- GetItem(MenuCmdHandle(CMD_HELP), MenuCmdItem(CMD_HELP), title);
- GetItemCmd(MenuCmdHandle(CMD_HELP), MenuCmdItem(CMD_HELP), &cmd);
- DelMenuItem(MenuCmdHandle(CMD_HELP), MenuCmdItem(CMD_HELP));
- AppendMenu(help, title);
- (*gMenuTable)[CMD_HELP].item = CountMItems(help);
- (*gMenuTable)[CMD_HELP].id = kHMHelpMenuID;
- (*gMenuTable)[CMD_HELP].menu = help;
- SetItemCmd(MenuCmdHandle(CMD_HELP), MenuCmdItem(CMD_HELP), cmd);
- }
- }
- }
-
- /* append the commands to the menu table */
- void MenuTableAppend(const MenuPickType *mt)
- {
- MenuPickType pick;
- short i, maxcmd;
-
- maxcmd = 0;
- for (i = 0; mt[i].id; i++)
- if (mt[i].cmd > maxcmd)
- maxcmd = mt[i].cmd;
- maxcmd++;
- if (! gMenuTable)
- gMenuTable = HandleBeginClear(sizeof(MenuPickType) * maxcmd);
- else {
- maxcmd = max(gMenuTableSize, maxcmd);
- HandleSizeSet(gMenuTable, sizeof(MenuPickType) * maxcmd);
- }
- gMenuTableSize = maxcmd;
- for (i = 0; mt[i].id; i++) {
- check(MenuPickValid(&mt[i]));
- pick = mt[i];
- pick.menu = MenuHandleGet(mt[i].id);
- (*gMenuTable)[mt[i].cmd] = pick;
- check(MenuPickValid(&pick));
- check(MenuValid(pick.menu));
- }
- MenuSetup();
- }
-
- /* set the menu table */
- void MenuTableSet(const MenuPickType *mt)
- {
- if (gMenuTable) {
- HandleEnd(gMenuTable);
- gMenuTable = NULL;
- gMenuTableSize = 0;
- }
- MenuTableAppend(mt);
- }
-
- /*---------------------------------------------------------------------------*/
- /* menu command and menu picks */
- /*---------------------------------------------------------------------------*/
-
- MenuPickType MenuPick(short id, short item)
- {
- const MenuPickType *mt = NULL;
- const MenuPickType *found = NULL;
- MenuPickType pick;
- MenuCommandType cmd;
-
- memclr(&pick, sizeof(pick));
- mt = *gMenuTable; /* dereferenced handle! */
- for (cmd = 0; cmd < gMenuTableSize; cmd++) {
- if (mt[cmd].id == id) {
- if (! found)
- found = mt + cmd;
- if (mt[cmd].item == item) {
- found = mt + cmd;
- break;
- }
- }
- }
- if (found) {
- pick = *found;
- pick.item = item;
- }
- ensure(found ? (MenuPickValid(&pick) && MenuValid(pick.menu)) : true);
- return(pick);
- }
-
- const MenuPickType *MenuCmdPick(MenuCommandType cmd)
- {
- require(MenuCmdValid(cmd));
- return(&(*gMenuTable)[cmd]);
- }
-
- short MenuCmdID(MenuCommandType cmd)
- {
- require(MenuCmdValid(cmd));
- return((*gMenuTable)[cmd].id);
- }
-
- short MenuCmdItem(MenuCommandType cmd)
- {
- require(MenuCmdValid(cmd));
- return((*gMenuTable)[cmd].item);
- }
-
- MenuHandle MenuCmdHandle(MenuCommandType cmd)
- {
- require(MenuCmdValid(cmd));
- return((*gMenuTable)[cmd].menu);
- }
-
- void MenuCmdEnable(MenuCommandType cmd)
- {
- MenuEnable(MenuCmdHandle(cmd), MenuCmdItem(cmd), true);
- }
-
- void MenuCmdDisable(MenuCommandType cmd)
- {
- MenuEnable(MenuCmdHandle(cmd), MenuCmdItem(cmd), false);
- }
-
- void MenuCmdCheck(MenuCommandType cmd, Boolean check)
- {
- CheckItem(MenuCmdHandle(cmd), MenuCmdItem(cmd), check);
- }
-
- /*---------------------------------------------------------------------------*/
- /* accessing menus */
- /*---------------------------------------------------------------------------*/
-
- /* this is the same as GetMHandle, except that if the menu ID is
- kHMHelpMenuID it returns the application's help menu (not the
- global help menu) */
- MenuHandle MenuHandleGet(short id)
- {
- MenuHandle helpmenu = NULL;
- MenuHandle result = NULL;
-
- if (id == kHMHelpMenuID) {
- if (MacHasHelpManager() && HMGetHelpMenuHandle(&helpmenu) == noErr)
- result = helpmenu;
- }
- else
- result = GetMHandle(id);
- ensure(! result || MenuValid(result));
- return(result);
- }
-
- /* return item number with given title, or 0 if not found */
- short MenuFindItem(MenuHandle menu, const CStr255 title)
- {
- short item; /* current item number */
- short nitems; /* number of items in menu */
- Str255 name; /* name of current item */
- Str255 ptitle; /* title of item being searched for */
-
- require(MenuValid(menu));
- require(StrValid(title, sizeof(CStr255)));
- item = 0;
- nitems = CountMItems(menu);
- c2pstr(strcpy((char*)ptitle, title));
- for (item = 1; item <= nitems; item++) {
- GetItem(menu, item, name);
- if (EqualString(ptitle, name, false, true))
- break;
- }
- item = (item <= nitems ? item : 0);
- ensure(item >= 0 && item <= CountMItems(menu));
- return(item);
- }
-
- /*---------------------------------------------------------------------------*/
- /* accessing the menu list */
- /*---------------------------------------------------------------------------*/
-
- /* A handle to this structure is stored in the MeunList low memory global
- (see Menu Manager chapter of IM-1) */
- typedef struct {
- short offset;
- short right;
- short unused;
- } MenuListHdr;
-
- /* return number of menus in the menu list */
- short MenuListCount(void)
- {
- return((**(MenuListHdr **)GetMenuList()).offset / 6);
- }
-
- /* Return a handle to the indexed menu in the menu list. The first
- menu (normally the Apple menu) has index 0. If the indexed menu
- doesn't exist NULL is returned. */
- MenuHandle MenuListGet(short index)
- {
- struct list {
- MenuHandle menu;
- short left;
- } *list;
- MenuHandle menu = NULL;
-
- if (0 <= index && index < MenuListCount()) {
- list = (struct list *) (*GetMenuList() + sizeof(MenuListHdr));
- menu = list[index].menu;
- }
- return(menu);
- }
-
- /*---------------------------------------------------------------------------*/
- /* enabling and disabling menus and adjusting menus */
- /*---------------------------------------------------------------------------*/
-
- /* enable or disable the menu item */
- void MenuEnable(MenuHandle menu, short item, Boolean enable)
- {
- require(MenuValid(menu));
- if (enable)
- EnableItem(menu, item);
- else
- DisableItem(menu, item);
- }
-
- /* true if the menu item is enabled */
- Boolean MenuEnabled(MenuHandle menu, short item)
- {
- require(MenuValid(menu));
- require(item >= 0 && item <= CountMItems(menu));
- return(item > 31 || ((**menu).enableFlags & (1 << item)) != 0);
- }
-
- /* Return a 32-bit flag for which the i'th bit is set if the i'th menu
- is enabled. */
- long MenuBarState(void)
- {
- short i = 0;
- short mlc = 0;
- long flags = 0;
-
- mlc = min(sizeof(long) * CHAR_BIT, MenuListCount());
- for (i = 0; i < mlc; i++) {
- if (((**MenuListGet(i)).enableFlags & 0x01))
- flags |= 1 << i;
- }
- return(flags);
- }
-
- /* Enable menus for which the corresponding bit of 'flags' is set and disable
- all other menus. */
- void MenuBarEnable(long flags)
- {
- short i = 0;
- short mlc = 0;
-
- mlc = min(sizeof(long) * CHAR_BIT, MenuListCount());
- for (i = 0; i < mlc; i++)
- MenuEnable(MenuListGet(i), 0, (flags & (1 << i)) != 0);
- }
-
- /* uncheck all the items in the menu */
- static void MenuUncheck(MenuHandle menu)
- {
- short i, nitems = CountMItems(menu);
-
- for (i = 1; i <= nitems; i++)
- CheckItem(menu, i, false);
- }
-
- /* disable all the menus and reset the undo item; call this before adjusting
- the menus */
- void MenuAdjust(void)
- {
- SignedByte state = 0;
- const MenuPickType *table;
- MenuCommandType cmd;
- Str255 title;
-
- state = HandleLockHi(gMenuTable);
- table = *gMenuTable;
- for (cmd = 0; cmd < gMenuTableSize; cmd++) {
- if (table[cmd].menu) {
- DisableItem(table[cmd].menu, table[cmd].item);
- CheckItem(table[cmd].menu, table[cmd].item, false);
- }
- }
- if (table[CMD_FONT].menu) MenuUncheck(table[CMD_FONT].menu);
- if (table[CMD_SIZE].menu) MenuUncheck(table[CMD_SIZE].menu);
- if (table[CMD_STYLE].menu) MenuUncheck(table[CMD_STYLE].menu);
- if (table[CMD_WINDOW].menu) MenuUncheck(table[CMD_WINDOW].menu);
- if (table[CMD_UNDO].menu) {
- GetIndString(title, RLS_UNDO, 1);
- SetItem(table[CMD_UNDO].menu, table[CMD_UNDO].item, title);
- }
- HandleRestore(gMenuTable, state);
- }
-
- /* Given a font family id and true in outlined, will outline all items in a size
- menu which actually exist in that font. If outlined is false, all items will
- be set to plain text, which is useful if you don't have any font info. */
- void MenuOutlineSizes(MenuHandle menu, short family, Boolean outlined)
- {
- int nitems; /* number of items in menu */
- int item; /* current item number */
- long size; /* size of current menu item */
- Str255 name; /* name of item */
- Boolean found; /* flag that we've already found the menu item */
-
- require(MenuValid(menu));
- found = false;
- nitems = CountMItems(menu);
- for (item = 1; item <= nitems; item++) {
- GetItem(menu, item, name);
- StringToNum(name, &size);
- if (outlined && RealFont(family, size))
- SetItemStyle(menu, item, outline);
- else
- SetItemStyle(menu, item, 0);
- }
- }
-
- /*---------------------------------------------------------------------------*/
- /* setting up menus */
- /*---------------------------------------------------------------------------*/
-
- /* Append the menu with the specified ID to the menu bar. After the menu
- is inserted into the menu list all heirarchical menus it contains are
- inserted. */
- void MenuAppend(short id)
- {
- MenuHandle menu = NULL; /* current menu */
- MenuHandle heir = NULL; /* heirarchical menu */
- short nitems = 0; /* number of items in current menu */
- short item = 0; /* current item number */
- short cmd = 0; /* item's command character */
- short mark = 0; /* item's mark character */
-
- /* load next menu and ensure it's unpurgeable */
- menu = (MenuHandle) ResGet('MENU', id);
- HNoPurge((Handle) menu);
- menu = GetMenu(id);
- FailNILRes(menu);
-
- /* append menu */
- InsertMenu(menu, 0);
-
- /* search for and insert all heirarchical menus */
- nitems = CountMItems(menu);
- for (item = 1; item <= nitems; item++) {
- GetItemCmd(menu, item, &cmd);
- if (cmd == hMenuCmd) {
- GetItemMark(menu, item, &mark);
- heir = GetMenu(mark);
- FailNILRes(heir);
- HNoPurge((Handle) heir);
- InsertMenu(heir, -1);
- }
- }
- }
-
- /* Create the menu bar. This should be very similar to the ToolBox routine
- GetNewMBar followed by a call to SetMenuBar. This function differs from
- GetNewMBar in that all 'DRVR' resources are inserted into the apple
- menu (assumed to be the first menu in the menu bar) and all heirarchical
- menus are automatically created and inserted into the menu list. */
- void MenuBarGet(short id)
- {
- short nmenus = 0; /* number of menus */
- short index = 0; /* index to menus */
- volatile Handle mbar = NULL; /* the menu bar resource */
- volatile SignedByte state = 0; /* saved state of resource handle */
-
- TRY {
-
- /* build menu bar */
- mbar = ResGet('MBAR', id);
- state = HandleNoPurge(mbar);
- nmenus = ((short *) *mbar)[0];
- for (index = 1; index <= nmenus; index++)
- MenuAppend(((short *) *mbar)[index]);
-
- /* build apple and font menus */
- if (GetMHandle(RLM_APPLE))
- AddResMenu(GetMHandle(RLM_APPLE), 'DRVR');
- if (GetMHandle(RLM_FONT))
- AddResMenu(GetMHandle(RLM_FONT), 'FOND');
-
- #ifndef NDEBUG
- /* append debug menu */
- if (RELEASE_LEVEL == RELEASE_DEV || KeyIsDown(optionKeyCode))
- MenuAppend(RLM_DEBUG);
- #endif /* NDEBUG */
- } CLEANUP {
- if (mbar)
- HandleRestore(mbar, state);
- } ENDTRY;
- }
-